package SSF.OS.TCP;

/**
 * Instrumentation.java
 *
 * Created: Mon Jul 19 02:24:52 1999 hongbol
 * Modified Sat Nov 20 1999 ato
 * @version 0.7.0
 */

import java.io.*;
/** 
 * Instrumentation class is used to monitor the internal state of one TCP
 * session at one end of the connection and to write data to a dumpfile.
 */
public class Instrumentation  {
  /**
   * the reference of the TCP session need to check the internal state
   */
  tcpSession tcpsess;
  tcpSessionMaster tcpMaster;

  /** print writer for output*/
  PrintWriter wr = null;

  int timeout;

  int fastrexit;

  /** event type*/
  static final public int EVT_OPEN = 0;
  static final public int EVT_CLOSE = 1;
  static final public int EVT_PKSND = 2; 
  static final public int EVT_PKRCV = 3; 
  static final public int EVT_SLOWTICK = 4; 
  static final public int EVT_FASTTICK = 5; 
  static final public int EVT_SOCKWRITE = 6; 
  static final public int EVT_SOCKREAD = 7; 
  static final public int EVT_STATE = 8; 
  /**
   * @param tcp tcpSession object represent one TCP session
   */ 
  public Instrumentation(tcpSessionMaster tcpms, tcpSession tcp) {
    String str;
    tcpsess = tcp;
    tcpMaster = tcpms;
    if((str = tcpMaster.eventDumpFile) != null) {
      str = str + "_" + ((SSF.Net.Host)tcpMaster.inGraph()).nhi 
            + "_" + tcpsess.flowID + ".out";
      try {
        wr = new PrintWriter(new FileOutputStream(str));
      } catch (IOException ioex) {
        ioex.printStackTrace(); 
      }
    }
    if(wr != null){
      wr.println("# WARNING: this file does not report what is actually in \n" + 
                 "# sent/received TCP packets. It reports values of various \n" +
                 "# state variables at the moment of reporting. \n" +
                 "# Use tcpdump to examine TCP/IP headers as sent/received. \n" +
                 "# \n");
      wr.println("#======================= Field Name =====================#");
      wr.println("# 1. flow ID:  unique within each host.\n" + 
                 "# 2. Event Type: \n" + 
                 "#      state_change(>) \n" + 
                 "#      packet_received(+),  packet_sent(-)\n" + 
                 "#      slow_tick(t), fast_tick(f)\n" + 
                 "#      app_read(r), app_write(w)\n" + 
                 "# 3. time (seconds)\n" + 
                 "# 4. TCP State: \n" +
                 "#      LN(LISTEN), SS(SYN_SENT), SR(SYN_RECEIVED) \n" + 
                 "#      ES(ESTABLISHED), CW(CLOSE_WAIT),\n" +
                 "#      CL(CLOSING), F1(FIN_WAIT_1), F2(FIN_WAIT_2), \n" +
                 "#      LA(LAST_ACK), TW(TIME_WAIT), CO(CLOSED) \n" +
                 "# 5. TCP sub_state: \n" + 
                 "#      U(undefined), S(slow_start),  \n" + 
                 "#      C(congestion_avoidance), F(fast recovery) \n" +
                 "# 6. Last sent sequence number (byte)\n" + 
                 "# 7. Send next sequence number (byte)\n" + 
                 "# 8. Last acked sequence number (byte)\n" + 
                 "# 9. Send buffer length (bytes)\n" +
                 "# 10. Remote advertised window (bytes)\n" +
                 "# 11.Duplicate ACKs \n" + 
                 "# 12.Congestion window size (bytes)\n" + 
                 "# 13.Slow start threshold (bytes)\n" + 
                 "# 14.Sequence number for RTT measurement (byte)\n" +
                 "# 15.Current RTT value for RTO estimation (seconds)\n" + 
                 "# 16.Current smoothed RTT value for RTO estimation" + 
                 " (seconds)\n" +
                 "# 17.Estimated RTO (seconds)\n" +
                 "# 18.RTO counter's value (ticks)\n" +
                 "# 19.Next expected sequence number to receive (byte)\n" +
                 "# 20.Last sequence number read byte application (byte)\n" + 
                 "# 21.Last received sequence number (byte)\n" + 
                 "# 22.Local advertised window (bytes)\n" +
                 "#========================================================#");
    }
  }

  /**
   * Show the current transition state of the TCP session
   */
  public void showTcpState(){
    String state;
    switch (tcpsess.tcp_state) {
    case 0:
      state = "CO";  //CLOSED";
      break;
    case 1:
      state = "LN";   //"LISTEN";
      break; 
    case 2:
      state = "SS";  //SYN_SENT";
      break;
    case 3:
      state = "SR";  //SYN_RECEIVED";
      break; 
    case 4:
      state = "ES";   //ESTABLISHED";
      break; 
    case 5:
      state = "CW";  //CLOSE_WAIT";
      break;
    case 6:
      state = "CL";  //CLOSING";
      break; 
    case 7:
      state = "F1";  //FIN_WAIT_1";
      break; 
    case 8:
      state = "F2";  //FIN_WAIT_2";
      break; 
    case 9:
      state = "LA";  //LAST_ACK";
      break; 
    case 10:
      state = "TW";  //TIME_WAIT";
      break; 
    default:
      state = "??";
      break;
    }   // end of switch (tcpsess.tcp_state)
    if(wr != null)
      wr.print(state);
  }

  /** 
   * list the values of all parameters used by one session 
   * at initaliztion
   */ 
  public void printInitialValue(){
    System.out.println("TCP Initial value:");
    System.out.println("source IP address = " + tcpsess.ipHdr.SOURCE_IP);
    System.out.println("destination IP address = " + tcpsess.ipHdr.DEST_IP);
    System.out.println("source port number = " + tcpsess.tcpHdr.SOURCE_port);
    System.out.println("destination port number = " + tcpsess.tcpHdr.DEST_port);
    System.out.println("Send buffer size = " + tcpsess.snd.SendBuffer.TCP_BUFFER_SPACE);
    System.out.println("MSS = " + tcpsess.snd.MSS);
    System.out.println("Initial Sequence NO = " + tcpsess.tcpServ.ISS);
    System.out.println("Receive buffer size = " + tcpsess.rcv.MAX_RCV_WND);
    System.out.println("MSL = " + tcpsess.tcpServ.MSL);
    System.out.println("MaxIdleTime = " + tcpsess.tcpServ.MaxIdleTime + "\n");
  }  

  /**
   * Print current send window parameters of TCP session
   */
  public void printSendWndParam(long time){

    System.out.print(time + " ");
    showTcpState();
    System.out.println(" " + tcpsess.snd.LastSent + 
                       " " + tcpsess.snd.SendNext + 
                       " " + (tcpsess.snd.FirstUnACKed - 1) +
                       " " + tcpsess.snd.AdvertisedWnd +
                       " " + tcpsess.snd.conWnd +
                       " " + tcpsess.snd.conThresh +
                       " " + tcpsess.snd.rttSeq +
                       " " + tcpsess.snd.rtt +
                       " " + tcpsess.snd.rttSmoothed +
                       " " + tcpsess.snd.rttVar +
                       " " + tcpsess.snd.rexTimeout);
  }


  /**
   * Print current receive window parameters of TCP session
   */
  public void printRcvWndParam(long time){
    System.out.print(time + " ");
    showTcpState();
    System.out.println(" " + tcpsess.rcv.NextExpected + 
                       " " + tcpsess.rcv.LastRead +
                       " " + tcpsess.rcv.LastRcvd +
                       " " + tcpsess.rcv.AdvertisedWnd);
  }

  public String showSubState(){
    String state;
    switch (tcpsess.snd.sub_state) {
    case 0:
      state = "U";  
      break;
    case 1:
      state = "S";
      break; 
    case 2:
      state = "C";
      break;
    case 3:
      state = "F";
      break; 
    default:
      state = "U";
      break;
    }
    return state;
  }

  public void takeSnapshot(float time, int evtType){
    if(wr == null)
      return;
    switch(evtType){
    case EVT_STATE:
      wr.print(tcpsess.flowID + "  >  " + time + " ");
      break;
    case EVT_OPEN:
      wr.print(tcpsess.flowID + "  o  " + time + " ");
      break;
    case EVT_CLOSE:
      wr.print(tcpsess.flowID + "  c  " + time + " ");
      break;
    case EVT_PKSND:
      wr.print(tcpsess.flowID + "  -  " + time + " ");
      break;
    case EVT_PKRCV:
      wr.print(tcpsess.flowID + "  +  " + time + " ");
      break; 
    case EVT_SLOWTICK:
      wr.print(tcpsess.flowID + "  t  " + time + " ");
      break; 
    case EVT_FASTTICK:
      wr.print(tcpsess.flowID + "  f  " + time + " ");
      break; 
    case EVT_SOCKWRITE:
      wr.print(tcpsess.flowID + "  w  " + time + " ");
      break; 
    case EVT_SOCKREAD:
      wr.print(tcpsess.flowID + "  r  " + time + " ");
      break; 
    default:
      break;
    }

    showTcpState();
    wr.println(" " + showSubState() + " " + tcpsess.snd.getSendWndParam() +
               " " + tcpsess.rcv.getRcvWndParam());
    wr.flush();
  }

  String sessionInfo(String str){
    return (tcpMaster.inGraph().now()/(float)SSF.Net.Net.seconds(1.0)
            + " TCP host " + ((SSF.Net.Host)tcpMaster.inGraph()).nhi
            + " " + SSF.Net.Util.IP_s.IPtoString(tcpMaster.ipAddr)
            + ":" + tcpsess.srcPort + " " + str);
  }
} // Instrumentation
